home *** CD-ROM | disk | FTP | other *** search
- /*
- File: ThreadedProgress.c
-
- Contains: Progress bar implementation using the Thread Manager
-
- Written by: Chris White, Developer Technical Support
-
- Copyright: © 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- 1/22/96 CW First release
-
- */
-
-
- #pragma segment Core
-
-
- // System Includes
-
-
- #include <Threads.h>
-
-
-
- // Application includes
-
- #ifndef __BAREBONES__
- #include "BareBones.h"
- #endif
-
- #ifndef __PROTOTYPES__
- #include "Prototypes.h"
- #endif
-
-
- // static prototypes
-
- static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo );
- static pascal void ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam );
-
-
-
-
-
- OSErr ThreadedProgressOperation ( tThreadedOperation theOperation, void* refCon,
- StringPtr theText, SInt32* operationErr, Boolean bBarberPole )
- {
- OSErr theErr = noErr;
- ThreadID operationsThreadID = 0;
- ThreadID progressDlgThreadID = 0;
- tThreadedOperationPtr theInfo = nil;
-
-
-
- theInfo = (tThreadedOperationPtr) NewPtrClear ( sizeof ( tThreadedOperationRec ) );
- theErr = MemError ( );
- if ( theErr ) goto CleanupAndbail;
-
- BlockMoveData ( theText, theInfo->theText, theText[0] + 1 );
- theInfo->bBarberPole = bBarberPole;
- theInfo->refCon = refCon;
-
-
- // Create the Progress Dialog Thread
- theErr = NewThread ( kCooperativeThread, (ThreadEntryProcPtr) ProgressDialogThread,
- (void*) theInfo, kDefaultStackSpace, kNoCreationOptions,
- (void*) operationErr, &progressDlgThreadID );
- if ( theErr ) goto CleanupAndbail;
-
- // Install termination routine to decrement the usage count
- // and maybe dispose of the tThreadedOperationRec.
- theInfo->usageCount++;
- theErr = SetThreadTerminator ( progressDlgThreadID, ThreadTermination, (void*) theInfo );
-
- // Create the Operation Thread
- theErr = NewThread ( kCooperativeThread, (pascal void* (*) (void*)) theOperation, (void*) theInfo,
- kDefaultStackSpace, kNoCreationOptions, (void*) operationErr,
- &operationsThreadID );
- if ( theErr ) goto CleanupAndbail;
-
- theInfo->usageCount++;
- theErr = SetThreadTerminator ( operationsThreadID, ThreadTermination, (void*) theInfo );
-
- // Get the dialog drawn _before_ the actual operation is started
- theErr = YieldToThread ( progressDlgThreadID );
- if ( theErr ) goto CleanupAndbail;
-
- return noErr;
-
- CleanupAndbail:
-
- if ( theInfo )
- DisposePtr ( (Ptr) theInfo );
-
- // Dispose of the threads, and pass the error codes back
- if ( operationsThreadID )
- DisposeThread ( operationsThreadID, (void*) theErr, false );
-
- if ( operationsThreadID )
- DisposeThread ( progressDlgThreadID, (void*) theErr, false );
-
- return theErr;
- }
-
-
-
- static pascal void* ProgressDialogThread ( tThreadedOperationPtr theInfo )
- {
- SInt16 theType;
- GrafPtr savePort;
- DialogRef theDialog = nil;
- Handle theHan;
- Rect theRect;
-
-
- theDialog = GetNewDialog ( kProgressDialogID, nil, (WindowPtr) -1 );
-
- SetWRefCon ( theDialog, (long) theInfo );
-
- GetDialogItem ( theDialog, kStaticTextItemID, &theType, &theHan, &theRect );
- SetDialogItemText ( theHan, theInfo->theText );
-
- GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect );
- if ( !theInfo->bBarberPole )
- {
- SetDialogItem ( theDialog, kUserItemID, theType, (Handle) gOutlineUserItemUPP, &theRect );
- CallUserItemProc ( gOutlineUserItemUPP, theDialog, kUserItemID );
- }
-
- ShowWindow ( theDialog );
- DrawDialog ( theDialog );
-
-
- while ( theInfo->bCancelled == false )
- {
- YieldToAnyThread ( );
-
- // Although the user interface doesn't allow you to execute
- // more than one threaded progress bar, we'll be careful to
- // support it here. It's just a case of making sure the port
- // is setup and restored across calls to YieldToAnyThread.
-
- GetPort ( &savePort );
- SetPort ( theDialog );
-
- if ( theInfo->bBarberPole )
- {
- PicHandle thePic;
- static SInt16 theID = 1000; // Gotcha: Non-reentrant, see documentation
-
-
- // Gotcha: Some Resource Manager calls can only be made
- // from the main thread on a Mac Plus. See documentation
- thePic = GetPicture ( theID++ );
- DrawPicture ( thePic, &theRect );
- if ( theID > 1003 )
- theID = 1000;
-
- if ( theInfo->doneAmount == kBarberPoleFinished )
- break;
- }
- else
- {
- if ( theInfo->doneAmount != theInfo->drawnAmount )
- {
- int theLength;
- float floatDone, floatMax, thePercent;
-
- // Temporarily adjust the user item rect to draw the bar
- GetDialogItem ( theDialog, kUserItemID, &theType, &theHan, &theRect );
- theLength = theRect.right - theRect.left;
- floatDone = theInfo->doneAmount;
- floatMax = theInfo->maxAmount;
- thePercent = (floatDone / floatMax) * 100;
- theRect.right = theRect.left + ((thePercent / 100) * theLength);
-
- theRect.top--; theRect.bottom++;
- FillRect ( &theRect, &qd.black );
- }
-
- if ( theInfo->doneAmount == theInfo->maxAmount )
- break;
- }
-
- SetPort ( savePort );
- }
-
- // A problem occurs if the thread is terminated before the dialog is
- // disposed. Fortunatly, with the current implementation, it can't.
- // However, if this can occur with your implementation, you'll want
- // to dispose of the dialog and (optionally?) restore the port in the
- // termination routine.
- DisposeDialog ( theDialog );
-
- return noErr;
- }
-
-
-
- //
- // We don't want to dispose of the record when another thread could still
- // be accessing it. Although they'll both finish about the same time, we
- // don't want to rely on the implementation of the thread scheduler. This
- // approach ensures both threads have finished with the record.
- //
- static pascal void ThreadTermination ( ThreadID threadTerminated, void* terminationProcParam )
- {
- OSErr theErr;
-
-
- #if DEBUGGING
- if ( terminationProcParam == nil )
- DebugStr ( "\p ThreadTermination: terminationProcParam is nil" );
- #endif
-
-
- ((tThreadedOperationPtr) terminationProcParam)->usageCount--;
- if ( ((tThreadedOperationPtr) terminationProcParam)->usageCount == 0 )
- {
- DisposePtr ( (Ptr) terminationProcParam );
-
- #if DEBUGGING
- theErr = MemError ( );
- if ( theErr ) DebugStrNum ( "\p ThreadTermination: DisposePtr", theErr );
- #endif
- }
-
- return;
- }
-
-
-
- //
- // This routine is one of the operations carried out
- // which the progress bar is representing.
- //
- pascal SInt32 ThreadedStandardDemoOperation ( tThreadedOperationPtr theInfo )
- {
- OSErr theErr = noErr;
- int i;
- const int max = 100;
-
-
- for ( i = 1; i <= max && theInfo->bCancelled == false; i++ )
- {
- SInt32 theDelay = 10L;
- Delay ( theDelay, &theDelay );
-
- theInfo->doneAmount = i;
- theInfo->maxAmount = max;
-
- YieldToAnyThread ( );
- }
-
- return (SInt32) theErr;
- }
-
-
-
- //
- // This routine is one of the operations carried out
- // which the progress bar is representing.
- //
- pascal SInt32 ThreadedBarberPoleDemoOperation ( tThreadedOperationPtr theInfo )
- {
- // A5 is not garanteed to be valid
-
- OSErr theErr = noErr;
- int i;
- const int max = 100;
-
-
- for ( i = 1; i <= max && theInfo->bCancelled == false; i++ )
- {
- SInt32 theDelay = 10L;
- Delay ( theDelay, &theDelay );
-
- YieldToAnyThread ( );
- }
-
- theInfo->doneAmount = kBarberPoleFinished;
-
- return (SInt32) theErr;
- }
-
-
-
-
-